﻿using System;
using System.Collections.Generic;
using System.Text;
using Xeml.Document.Contracts;
using Xeml.Document.EnvironmentalOntology;
using System.Diagnostics;
using System.Net;
using System.IO;
using Xeml.Document.Manager;
using System.Xml;
using System.Xml.XPath;
using Xeml.Document.Ontologies.Positioning;

namespace Xeml.Document.Ontology
{
    public class PositioningOntologyHandler : IVariableOntologyHandler, IHierarchicalView
    {
        public static readonly string URI = "http://www.mpimp-golm.mpg.de/2009/XEML/Positioning";

        #region IVariableOntologyHandler Members

        private List<IVariableOntologyTerm> _terms;
        public IEnumerable<IVariableOntologyTerm> Terms
        {
            get { return _terms; }
        }

        public IVariableOntologyTerm Find(Term param)
        {
            throw new NotImplementedException();
        }

        public IVariableOntologyTerm Find(string termId)
        {
            throw new NotImplementedException();
        }
        #endregion

        #region IOntologyHandler Members

        public string NameSpace { get; private set; }

        public bool AutoValidation
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public string _instanceLocation;
        public string InstanceLocation
        {
            get
            {
                return _instanceLocation;
            }
            set
            {
                if (_instanceLocation != value)
                {
                    _ontologyLoadState = OntologyLoadState.ReloadNeeded;
                    _ontologyLoadMessage = "Reload needed";
                    _instanceLocation = value;
                }
            }
        }

        private string _ontologyLoadMessage;
        public string OntologyLoadMessage
        {
            get { return _ontologyLoadMessage; }
        }

        private OntologyLoadState _ontologyLoadState = OntologyLoadState.Unloaded;
        public OntologyLoadState OntologyLoadState
        {
            get { return _ontologyLoadState; }
        }

        public OntologyType OntologyType
        {
            get { return OntologyType.Positioning; }
        }

        public void Load(string nameSpaceAlias)
        {
            Load(nameSpaceAlias, false);
        }

        public void Load(string nameSpaceAlias, bool force)
        {
            if (_alias != nameSpaceAlias)
            {
                _alias = nameSpaceAlias;
                _ontologyLoadState = OntologyLoadState.ReloadNeeded;
            }
            if (_ontologyLoadState == OntologyLoadState.ReloadNeeded || force)
            {

                Debug.WriteLine("PositioningOntologyHandler. Begin ontology load:" + _instanceLocation);
                _terms.Clear();
                _globalUnits.Clear();
                _globalTypes.Clear();
                _contextCollection.Clear();

                WebClient wc = new WebClient();

                TextReader txr = new StringReader(OnlineCacheManager.Instance.DownloadContent(_instanceLocation));

                XmlDocument xpd = new XmlDocument();
                xpd.Load(txr);
                Init(xpd.CreateNavigator());
                BuildHirachy();

                _ontologyLoadState = OntologyLoadState.Loaded;

                Debug.WriteLine("PositioningOntologyHandler. End ontology load:" + _instanceLocation);
            }
        }

        private void BuildHirachy()
        {
            foreach (PosTerm item in Terms)
            {
                TermNode currentNode = null;
                for (int i = 0; i < item.GroupLevels.Length; i++)
                {
                    TermNode nextNode = FindSubNode(currentNode, item.GroupLevels[i], "subgroup_of");
                    if (nextNode == null)
                    {
                        nextNode = TermNode.CreateContainerNode(item.GroupLevels[i]);
                        if (i == 0)
                            _nodes.Add(nextNode);
                        else
                            currentNode.AddSubNode(nextNode, "subgroup_of");
                    }
                    currentNode = nextNode;
                }
                TermNode leaf = TermNode.CreateNode(item);
                currentNode.AddSubNode(leaf, "belongs_to");
            }
        }

        private TermNode FindSubNode(TermNode tn, string label, string attribute)
        {
            if (tn == null)
            {
                foreach (var item in this.Nodes)
                {
                    if (item.Label == label)
                        return item;
                }
            }
            else
            {
                foreach (var item in tn.Childs)
                {
                    if (item.Attribute == attribute && item.Relation.Label == label)
                        return item.Relation;
                }
            }
            return null;
        }
        #endregion

        #region IValidationSource Members

        protected virtual void OnValidation(ValidationMessage vm)
        {
            if (Validation != null)
                Validation(this, vm);
        }
        public event EventHandler<ValidationMessage> Validation;

        #endregion

        #region IXemlComponent Members

        public string Publisher
        {
            get { return "Max Planck Institute of Molecular Plant Physiology, Potsdam/Golm, Germany"; }
        }

        private Version version = new Version(1, 1, 0);
        public Version Version
        {
            get { return version; }
        }

        public string ComponentName
        {
            get { return "Xeml Positioning Ontology Handler"; }
        }

        public string Author
        {
            get { return "Jan Hannemann"; }
        }

        public string Description
        {
            get { return "The XEML framework standard component for the sample positioning information. This component handles ontologies of the XEO Xml dialect."; }
        }

        public string Uri
        {
            get { return URI; }
        }

        public IXemlComponent Copy()
        {
            return new PositioningOntologyHandler();
        }

        #endregion

        public PositioningOntologyHandler()
        {
            _terms = new List<IVariableOntologyTerm>();
            //  _readOnlySupervisors = new ReadOnlyCollection<IVariableSupervisor>(this._varSpecs);
            _globalTypes = new Dictionary<string, NamedTypeSpecification>();
            _globalUnits = new Dictionary<string, UnitSet>();
            _contextCollection = new Dictionary<string, string>();
        }

        #region private stuff
        Dictionary<string, NamedTypeSpecification> _globalTypes;
        Dictionary<string, UnitSet> _globalUnits;
        Dictionary<string, string> _contextCollection;
        private string _alias = "";
        private const string xmlNs = "http://www.mpimp-golm.mpg.de/2007/XEML/Environment";

        private void Init(XPathNavigator xpn)
        {
            xpn.MoveToRoot();
            xpn.MoveToChild("Repository", xmlNs);
            NameSpace = xpn.GetAttribute("NameSpace", "");

            XPathNavigator resourcesNav = xpn.Clone();
            if (resourcesNav.MoveToChild("Resources", xmlNs))
            {
                XPathNavigator globalTypesNav = resourcesNav.Clone();
                if (globalTypesNav.MoveToChild("TypeSets", xmlNs))
                    InitGlobalType(globalTypesNav);

                XPathNavigator globalUnitsNav = resourcesNav.Clone();
                if (globalUnitsNav.MoveToChild("UnitSets", xmlNs))
                    InitGlobalUnits(globalUnitsNav);

                XPathNavigator contextListNav = resourcesNav.Clone();
                if (contextListNav.MoveToChild("ContextCollection", xmlNs))
                    InitContextList(contextListNav);

            }

            XPathNodeIterator xni = xpn.SelectChildren("Variable", xmlNs);
            while (xni.MoveNext())
            {
                string termId = xni.Current.GetAttribute("TermId", "");
                PosTerm paramSpec = new PosTerm(termId, NameSpace, _alias);
                paramSpec.Name = xni.Current.GetAttribute("Name", "");
                paramSpec.GroupPath = xni.Current.GetAttribute("Group", "");
                         paramSpec.RecommendationLevel = XmlConvert.ToInt32(xni.Current.GetAttribute("RecommendationLevel", ""));


                if (xni.Current.MoveToChild("Definition", xmlNs))
                {
                    paramSpec.Definition = xni.Current.Value;
                    xni.Current.MoveToParent();
                }

                if (xni.Current.MoveToChild("CommunityUrl", xmlNs))
                {
                   // paramSpec.URL = new Uri(xni.Current.Value);
                    xni.Current.MoveToParent();
                }

                XPathNodeIterator xniC = xni.Current.SelectChildren("Context", xmlNs);
                while (xniC.MoveNext())
                {
                    VariableContextSpec pc = new VariableContextSpec();
                    XPathNavigator clone;
                    pc.Name = xniC.Current.GetAttribute("Name", "");
                    pc.Description = _contextCollection[pc.Name];

                    clone = xniC.Current.Clone();
                    InitTypeDefine(clone, pc);

                    clone = xniC.Current.Clone();
                    InitUnits(clone, pc);

                    clone = xniC.Current.Clone();
                    if (clone.MoveToChild("DefaultValue", xmlNs))
                    {
                        pc.DefaultValue = clone.Value;
                    }

                    //clone = xniC.Current.Clone();
                    //if (clone.MoveToChild("DefaultCycle", xmlNs))
                    //{
                    //    InitCycle(clone, pc);
                    //}
                    paramSpec.ContextCollection.Add(pc.Name, pc);

                }
                _terms.Add(paramSpec);
                paramSpec.Validation += new EventHandler<ValidationMessage>(paramSpec_Validation);
            }
        }

        void paramSpec_Validation(object sender, ValidationMessage e)
        {
            OnValidation(e);
        }

        private void InitContextList(XPathNavigator contextListNav)
        {
            XPathNodeIterator xni = contextListNav.SelectChildren("Context", xmlNs);
            while (xni.MoveNext())
            {
                string cName = xni.Current.GetAttribute("Name", "");
                string desc = "";
                XPathNavigator xpn = xni.Current.Clone();
                if (xpn.MoveToChild("Description", xmlNs))
                    desc = xpn.Value;
                _contextCollection.Add(cName, desc);

            }
        }

        private void InitGlobalType(XPathNavigator globalTypesNav)
        {
            XPathNodeIterator xni = globalTypesNav.SelectChildren("TypeDefine", xmlNs);
            while (xni.MoveNext())
            {
                NamedTypeSpecification nts = new NamedTypeSpecification();
                InitTypeDefine(xni.Current.Clone(), nts);
                nts.Name = xni.Current.GetAttribute("Name", "");
                if (_globalTypes.ContainsKey(nts.Name))
                    _globalTypes.Remove(nts.Name);
                _globalTypes.Add(nts.Name, nts);
            }
        }

        private void InitGlobalUnits(XPathNavigator globalTypesNav)
        {
            XPathNodeIterator xni = globalTypesNav.SelectChildren("Units", xmlNs);
            while (xni.MoveNext())
            {
                XPathNavigator xpn = xni.Current.Clone();
                UnitSet us = new UnitSet();
                us.Name = xpn.GetAttribute("Name", "");

                InitUnits(xpn, us);
                _globalUnits.Add(us.Name, us);
            }

        }


        private void InitUnits(XPathNavigator xpn, VariableContextSpec pc)
        {
            UnitSet us = new UnitSet();
            if (xpn.MoveToChild("UnitsSource", xmlNs))
            {
                if (!_globalUnits.TryGetValue(xpn.GetAttribute("unitRef", ""), out us))
                    us = new UnitSet();

            }
            else if (xpn.MoveToChild("Units", xmlNs))
            {
                InitUnits(xpn, us);
            }


            pc.UnitSet = us;


        }

        private void InitUnits(XPathNavigator xpn, UnitSet us)
        {
            if (xpn.MoveToChild("Default", xmlNs))
            {
                Unit u = new Unit();
                u.Name = xpn.GetAttribute("Name", "");
                u.Symbol = xpn.GetAttribute("Symbol", "");
                us.DefaultUnit = u;
                while (xpn.MoveToNext("Unit", xmlNs))
                {
                    ConvertableUnit cu = new ConvertableUnit();
                    cu.Name = xpn.GetAttribute("Name", "");
                    cu.Symbol = xpn.GetAttribute("Symbol", "");
                    cu.ConversionString = xpn.GetAttribute("Conversion", "");
                    cu.DefaultUnit = u;
                    us.ConvertableUnits.Add(cu);
                }
            }

        }



        private void InitTypeDefine(XPathNavigator xpn, TypeSpecification td)
        {

            string basetype = xpn.GetAttribute("BaseType", "");
            switch (basetype)
            {
                case "Number":
                    {
                        td.BaseType = VariableDataTypes.Number;
                        break;
                    }
                case "Text":
                    {
                        td.BaseType = VariableDataTypes.Text;
                        break;
                    }
                case "Date":
                    {
                        td.BaseType = VariableDataTypes.Date;
                        break;
                    }
                case "Boolean":
                    {
                        td.BaseType = VariableDataTypes.Bool;
                        break;
                    }
                default:
                    {
                        td.BaseType = VariableDataTypes.Text;
                        break;
                    }
            }

            if (xpn.MoveToFirstChild())
            {
                do
                {
                    TypeSpecifics ts = null;


                    switch (xpn.LocalName)
                    {
                        case "Min":
                            {
                                ts = new Min();
                                break;
                            }
                        case "Max":
                            {
                                ts = new Max();
                                break;
                            }
                        case "Enum":
                            {
                                ts = new Xeml.Document.EnvironmentalOntology.Enum();
                                break;
                            }
                        case "Exclude":
                            {
                                ts = new Exclude();
                                break;
                            }
                        default:
                            break;
                    }

                    if (ts != null)
                    {
                        ts.TextVal = xpn.Value;

                        switch (td.BaseType)
                        {
                            case VariableDataTypes.Number:
                                {
                                    ts.NumberVal = (decimal)xpn.ValueAsDouble;
                                    break;
                                }
                            case VariableDataTypes.Text:
                                {
                                    ts.TextVal = xpn.Value;
                                    break;
                                }
                            case VariableDataTypes.Date:
                                {
                                    ts.DateVal = xpn.ValueAsDateTime;
                                    break;
                                }
                            case VariableDataTypes.Bool:
                                {
                                    ts.BooleanVal = xpn.ValueAsBoolean;
                                    break;
                                }
                            default:
                                {
                                    ts.TextVal = xpn.Value;
                                    break;
                                }
                        }

                        td.TypeSpecifica.Add(ts);
                    }

                } while (xpn.MoveToNext());
            }



        }

        private void InitTypeDefine(XPathNavigator xpn, VariableContextSpec pc)
        {
            TypeSpecification ts = new TypeSpecification();
            if (xpn.MoveToChild("TypeSource", xmlNs))
            {
                NamedTypeSpecification nts;
                if (_globalTypes.TryGetValue(xpn.GetAttribute("typeRef", ""), out nts))
                    ts = nts;
            }
            else if (xpn.MoveToChild("TypeDefine", xmlNs))
            {
                InitTypeDefine(xpn, ts);
            }





            pc.TypeDefine = ts;
        }


        #endregion

        #region IHirachicalView Members

        private List<TermNode> _nodes = new List<TermNode>();
        public IEnumerable<TermNode> Nodes
        {
            get { return _nodes; }
        }

        public TermNode FindNode(IOntologyTerm t)
        {
            return FindNode(t.TermId);

        }

        public TermNode FindNode(string termid)
        {
            foreach (var item in _nodes)
            {
                if (FindNode(item, termid) != null)
                    return FindNode(item, termid);
            }
            return null;
        }

        private TermNode FindNode(TermNode start, string termid)
        {
            TermNode ret = null;
            if (!start.IsContainer && start.Term.TermId == termid)
                ret = start;
            else if (start.IsContainer)
                foreach (var item in start.Childs)
                {
                    if (FindNode(item.Relation, termid) != null)
                        ret = FindNode(item.Relation, termid);
                }

            return ret;
        }

        #endregion


        public event LoadedEventHandler OnLoaded;
    }
}
